#include "Connector.h"
#include "DSTHType.h"
#include "EventLoop.h"
#include "Log.h"

namespace dsth {

Connector::Connector(EventLoop* loop, const InetSocketAddress& svc_addr) 
    : loop_(loop),
    conn_socket_(),
    channel_(conn_socket_.GetFd(), loop),
    svc_addr_(svc_addr) {
    channel_.SetWriteHandler(std::make_pair(this, &Connector::OnWriteEnable));
}


Connector::~Connector() {
}

void Connector::Connect() {
    int ret = conn_socket_.Connect(svc_addr_);
    if (ret == 0) {
        // QUESTION: never ever go there.
        LOG_DEBUG("[Connector::Connect] return 0.");
        EstableishConnection();
    }
    else {
#ifdef IS_WINDOWS
        ret = ::WSAGetLastError();
        switch (ret) {
        case WSAEWOULDBLOCK:
        case WSAEINPROGRESS:
        case WSAEINTR:
        case WSAEISCONN:
            LOG_DEBUG("[Connector::Connect] return :%d.", ret);
            channel_.EnableWriting();
            break;
        case WSAECONNREFUSED:
            LOG_INFO("[Connector::Connect] WSAECONNREFUSED, errorcode:%d.", ret);
            refused_handler_();
            break;
        default:
            // TODO: may retry
            LOG_ERROR("[Connector::Connect] failed, errorcode:%d.", ret);
            break;
        }
#else
        ret = errno;
        switch (ret) {
        //case WSAEWOULDBLOCK:
        case EINPROGRESS:
        case EINTR:
        case EISCONN:
            LOG_DEBUG("[Connector::Connect] return :%d.", ret);
            channel_.EnableWriting();
            break;
        default:
            // TODO: may retry
            LOG_ERROR("[Connector::Connect] failed, errorcode:%d.", ret);
            break;
        }
#endif

    }
}

void Connector::EstableishConnection() {
    LOG_TRACE("[Connector::EstableishConnection]");
    if (conn_handler_) {
        conn_handler_(conn_socket_.GetFd());
    }
}

void Connector::OnWriteEnable() {
    LOG_TRACE("[Connector::HandleWrite]");
    DestroyChannel();
    int ret = net::GetSockErr(conn_socket_.GetFd());
    if (ret < 0) {
        //TODO:retry
        LOG_ERROR("[Connect::HandleWrite] getsockopt failed, errorcode:%d.", ret);
        return;
    }
    EstableishConnection();
}

void Connector::DestroyChannel() {
    assert(loop_->IsInLoopThread());
    channel_.DisableAllEvent();
    loop_->Remove(&channel_);
}

} // namespace
